﻿using System;
using System.Collections.Generic;
using System.Text;

namespace CodePlex.Resourceful.Microsoft.SSDS
{


    public abstract class SSDSE
    {

        // properties

        public static SSDSP<string> Id
        {
            get
            {
                return new MetadataP<string>("Id");
            }
        }
        public static SSDSP<decimal> Version
        {
            get
            {
                return new MetadataP<decimal>("Version");
            }
        }
        public static SSDSP<string> Kind
        {
            get
            {
                return new MetadataP<string>("Kind");
            }
        }

        public static SSDSP<T> Property<T>(string name)
        {
            return new FlexibleP<T>(name);
        }

        // >, >=, <, <=, ==, != 

        public static SSDSB Gteq<T>(SSDSP<T> property, SSDSC<T> constant)
        {
            return new GteqE<T>(property, constant);
        }
        public static SSDSB Gteq<T>(SSDSP<T> property, T constant)
        {
            return Promote<T>(Gteq, property, constant);
        }

        public static SSDSB Lteq<T>(SSDSP<T> property, SSDSC<T> constant)
        {
            return new LteqE<T>(property, constant);
        }
        public static SSDSB Lteq<T>(SSDSP<T> property, T constant)
        {
            return Promote<T>(Lteq, property, constant);
        }

        public static SSDSB Gt<T>(SSDSP<T> property, SSDSC<T> constant)
        {
            return new GtE<T>(property, constant);
        }
        public static SSDSB Gt<T>(SSDSP<T> property, T constant)
        {
            return Promote<T>(Gt, property, constant);
        }

        public static SSDSB Lt<T>(SSDSP<T> property, SSDSC<T> constant)
        {
            return new LtE<T>(property, constant);
        }
        public static SSDSB Lt<T>(SSDSP<T> property, T constant)
        {
            return Promote<T>(Lt, property,constant);
        }

        public static SSDSB Eq<T>(SSDSP<T> property, SSDSC<T> constant)
        {
            return new EqE<T>(property, constant);
        }
        public static SSDSB Eq<T>(SSDSP<T> property, T constant)
        {
            return Promote<T>(Eq, property, constant);
        }

        public static SSDSB Neq<T>(SSDSP<T> property, SSDSC<T> constant)
        {
            return new NeqE<T>(property, constant);
        }
        public static SSDSB Neq<T>(SSDSP<T> property, T constant)
        {
            return Promote<T>(Neq, property, constant);
        }

        private static SSDSB Promote<T>(Func20<SSDSP<T>, SSDSC<T>, SSDSB> fn, SSDSP<T> property, T constant)
        {
            if (typeof(T).Equals(typeof(string)))
            {
                return fn(property, (SSDSC<T>)((object)SSDSE.String((string)((object)constant))));
            }
            if (typeof(T).Equals(typeof(decimal)))
            {
                return fn(property, (SSDSC<T>)((object)SSDSE.Decimal((decimal)((object)constant))));
            }
            if (typeof(T).Equals(typeof(DateTime)))
            {
                return fn(property, (SSDSC<T>)((object)SSDSE.DateTime((DateTime)((object)constant))));
            }
            if (typeof(T).Equals(typeof(bool)))
            {
                return fn(property, (SSDSC<T>)((object)(((bool)((object)constant)) ? SSDSE.True : SSDSE.False)));
            }

            throw new NotImplementedException("unsupported type");
        }
      




        // constant literals

        public static SSDSC<string> String(string value)
        {
            return new StringC(value);
        }

        public static SSDSC<decimal> Decimal(decimal value)
        {
            return new DecimalC(value);
        }

        public static SSDSC<DateTime> DateTime(DateTime value)
        {
            return new DateTimeC(value);
        }

        public static SSDSB False
        {
            get
            {
                return new BooleanLiteralQ(false);
            }
        }
        public static SSDSB True
        {
            get
            {
                return new BooleanLiteralQ(true);
            }
        }

        // && (and), || (or),! (not)
        public static SSDSB Not(SSDSB value)
        {
            return new NotQ(value);
        }

        public static SSDSB And(SSDSB lhs, SSDSB rhs)
        {
            return new AndQ(lhs, rhs);
        }

        public static SSDSB Or(SSDSB lhs, SSDSB rhs)
        {
            return new OrQ(lhs, rhs);
        }


      
        public abstract string ToQueryString();

    }
    public abstract class SSDSC<T> : SSDSE
    {

    }

    public abstract class SSDSP<T> : SSDSE
    {

    }


    public abstract class SSDSB : SSDSC<bool>
    {

    }


    public class FlexibleP<T>: SSDSP<T>
    {
        private readonly string _name;
        public FlexibleP(string name)
        {
            _name = name;
        }

        public override string ToQueryString()
        {
            return string.Format(@"e[""{0}""]", _name);
        }
    }

    public class MetadataP<T> : SSDSP<T>
    {
        private readonly string _value;
        public MetadataP(string value)
        {
            _value = value;
        }

        public override string ToQueryString()
        {
            return string.Format(@"e.{0}", _value);
        }
    }

    public class StringC : SSDSC<string>
    {
        private readonly string _value;
        public StringC(string value)
        {
            _value = value;
        }

        public override string ToQueryString()
        {
            return string.Format(@"""{0}""", _value);
        }
    }

    public class DecimalC : SSDSC<decimal>
    {
        private readonly decimal _value;
        public DecimalC(decimal value)
        {
            _value = value;
        }

        public override string ToQueryString()
        {
            return string.Format(@"{0}", _value);
        }
    }

    public class DateTimeC : SSDSC<DateTime>
    {
        private readonly DateTime _value;
        public DateTimeC(DateTime value)
        {
            _value = value;
        }

        public override string ToQueryString()
        {
            return string.Format(@"DateTime(""{0}"")", Utils.Iso8601Date(_value));
        }
    }
    public class GteqE<T> : BinaryQ
    {
        public GteqE(SSDSP<T> lhs, SSDSC<T> rhs) : base(lhs, rhs, ">=") { }
    }
    public class LteqE<T> : BinaryQ
    {
        public LteqE(SSDSP<T> lhs, SSDSC<T> rhs) : base(lhs, rhs, "<=") { }
    }
    public class GtE<T> : BinaryQ
    {
        public GtE(SSDSP<T> lhs, SSDSC<T> rhs) : base(lhs, rhs, ">") { }
    }
    public class LtE<T> : BinaryQ
    {
        public LtE(SSDSP<T> lhs, SSDSC<T> rhs) : base(lhs, rhs, "<") { }
    }
    public class EqE<T> : BinaryQ
    {
        public EqE(SSDSP<T> lhs, SSDSC<T> rhs) : base(lhs, rhs, "==") { }
    }
    public class NeqE<T> : BinaryQ
    {
        public NeqE(SSDSP<T> lhs, SSDSC<T> rhs) : base(lhs, rhs, "!=") { }
    }


  

    public class AndQ : BinaryQ
    {
        public AndQ(SSDSB lhs, SSDSB rhs) : base(lhs, rhs, "&&") { }
    }
    public class OrQ : BinaryQ
    {
        public OrQ(SSDSB lhs, SSDSB rhs) : base(lhs, rhs, "||") { }
    }
    public class BinaryQ : SSDSB
    {
        private readonly SSDSE _lhs;
        private readonly SSDSE _rhs;

        private readonly string _op;
        public BinaryQ(SSDSE lhs, SSDSE rhs, string op)
        {
            _lhs = lhs;
            _rhs = rhs;
            _op = op;
        }

        public override string ToQueryString()
        {
            return string.Format("({0}) {1} ({2})", _lhs.ToQueryString(),_op, _rhs.ToQueryString());
        }
    }


    public class NotQ : SSDSB
    {
        private readonly SSDSB _value;
        public NotQ(SSDSB value)
        {
            _value = value;
        }

        public override string ToQueryString()
        {
            return string.Format("!({0})", _value.ToQueryString());
        }
    }


    public class BooleanLiteralQ : SSDSB
    {
        private readonly bool _value;
        public BooleanLiteralQ(bool value)
        {
            _value = value;
        }

        public override string ToQueryString()
        {
            return _value.ToString().ToLower();
        }
    }



    
}
